iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
Software Development

Laravel 後端菜鳥可以知道的流程概念系列 第 20

Laravel filesystem / 專案筆記:團購圖片上傳功能

  • 分享至 

  • xImage
  •  

Laravel filesystem

概念

laravel檔案系統使用 flysystem ,預設可將檔案存在本地端、SFTP或是AWS S3,並可輕鬆切換

https://ithelp.ithome.com.tw/upload/images/20231004/20162893QthtsPshiq.png

  1. config/filesystems.php

    • config/filesystems.php:Laravel filesystem 的設定檔,可設定不同 disk (儲存位置) 。
    • 預設 disk 包含 local(本地端開發) 、public (專案上線前後) 、 AWS s3 (AWS空間),當然要設定其他的(例如: Google Cloud Platform)也是可以的
  2. php artisan storage:link

    這個 Artisan 命令用於創建符號連結,將 config/filesystems.php 內指定的路徑(預設是 public/storage) 連結到 storage/app/public 目錄,讓公開檔案可讓瀏覽器在 public 讀取。

  3. Illuminate\Support\Facades\Storage 有多種方法可以輕易地對檔案進行操作,例如:

    • put:將內容存儲到檔案。
    • get:讀取檔案的內容。
    • delete:刪除檔案。
    • exists:檢查檔案是否存在。
    • copy:複製檔案。
    • move:移動檔案。
    • size:獲取文件大小。
    • url:獲取檔案的公開 URL。
  4. League\Flysystem\UnableToWriteFile

    如果嘗試使用 Storage 對檔案進行操作時發生異常,可設定拋出League\Flysystem\UnableToWriteFile 實體


config/filesystems.php 文件設定

預設 disk

  • 預設的 disk 被寫在這裡,會套用到 .env 檔案內的設定

        /*
        |--------------------------------------------------------------------------
        | Default Filesystem Disk
        |--------------------------------------------------------------------------
        */
        'default' => env('FILESYSTEM_DISK', 'local'),
    
  • .env 檔案,建議在 .env 檔案改為 public
    本地端開發 local 及 public 皆可,專案上線後因為要讓部分檔案可公開讓大家儲存,就會需要改成 public

    FILESYSTEM_DISK=public
    

內建的 disk

除了預設的 disk 外,也可在這裡設定自己想要的 disk

/*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    */
    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
								// 檔案會被放在 storage/app 中
            'throw' => false,
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
								// 檔案會被放在 storage/app/public
            'visibility' => 'public',
            'throw' => false,
        ],

        's3' => [  //AWS s3 儲存空間服務
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw' => false,
        ],
    ],

設定要連結的位置

https://ithelp.ithome.com.tw/upload/images/20231004/20162893AQ0BppwoPl.png
可以用 php artisan storage:link 建立連結,建立連結後,無論檔案放在哪個資料夾,都可在另一個資料夾中讀取到

    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    */
    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],

團購圖片功能開發

需求

我希望每個團購都能有一張代表圖片,會在首頁、查看團購時顯示,使用者在新增團購時可上傳圖片,如果沒上傳就用預設圖片代替。

實現流程

https://ithelp.ithome.com.tw/upload/images/20231004/20162893cG2M1cEMoH.png

  1. 前端畫面加入上傳圖片功能, 標籤要加入 enctype="multipart/form-data”才能傳檔案(使用 bootstrap5,其他前端方法可能不同)

    <form action="/group" method="POST" enctype="multipart/form-data">
    	//...
    	<div class="mb-3">
    		<label for="group_image" class="form-label">上傳團購圖片</label>
    		<input name="group_image" class="form-control" 
                    type="file" id="group_image">
    	</div>
    </form>
    

    https://ithelp.ithome.com.tw/upload/images/20231004/20162893MMUvPHZKe6.png

  2. GroupController 加入圖片驗證邏輯,將圖片路徑一起存入資料庫

    public function store(Request $request)
    {
    	$validated = $request->validate([
    		'group_name'           => 'required|string|min:3|max:50',
            'organizer_id'         => 'required|int',
            'close_price'          => 'nullable|decimal:0,2',
            'close_date'           => 'nullable|date|after_or_equal:today',
            'allow_insert_product' => 'nullable',
            'status'               => 'required|int',
            'group_image'          => 
                           'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'
    						// 加入圖片驗證邏輯
        ]);
    
        abort_if(
            !$validated['close_date'] && !$validated['close_price'],
            Response::HTTP_BAD_REQUEST,
            __('請設定團購截止條件')
        );
    
        if ($validated['group_image']) {
            $validated['image_path'] = $validated['group_image']
                ->store('public/groups');
            // 因為 database 裡面只能存路徑,而非存檔案,要把檔案先儲存後取得路徑
        } else {
            $validated['image_path'] = "揪團囉.png";
    		// 如果使用者沒有上傳圖片,就給他我預設的檔案路徑
        }
    
        $group = Group::create($validated);
            // 路徑與資料一起存入資料庫
        return redirect(url("/group/$group->id/product/create"));
    
        }
    

    https://ithelp.ithome.com.tw/upload/images/20231004/201628936YT809bfBw.png

  3. 在前端畫面中顯示

    //controller
    public function create(Group $group)
    {
    		$group['image_path'] = Storage::url($group['image_path']);
    				// 將路徑用 Storage::url() 轉換為前端可讀取的位置
    				// dd()結果如圖
    		return view('group.createProduct', [
    				'group' => $group,
    
    		]);
    }
    

    https://ithelp.ithome.com.tw/upload/images/20231004/201628934SEfzRLEAW.png

    前端畫面加入顯示圖片,就可以正常顯示囉!

    // view
    <div class="mb-3">
    		<img src="{{$group->image_path}}" class="rounded img-fluid" alt="group_image">
    </div>
    

    https://ithelp.ithome.com.tw/upload/images/20231004/20162893CHtZ8NaOpN.png


菜鳥問問

公開檔案只能放在 public 資料夾?

公開檔案(例如:前端畫面、圖片、index.php 等檔案),因為需要讓大家都能存取,程式才能正常運作,因此通常統一會放在 public 資料夾內。而其他非公開檔案(如:.env檔案、controller檔案、model 檔案)則不會公開讓所有人可以存取。

  1. 安全性: public 資料夾是唯讀的,外部用戶無法直接訪問其中的 PHP 或 config 等配置文件,可以減少潛在的安全漏洞風險,並確保只有公開的檔案可以通過網頁瀏覽器訪問。
  2. 直接訪問性:將檔案存儲在 public 資料夾中,使它們可以直接透過瀏覽器訪問,而無需經過應用程序的處理。這對於顯示圖片、JavaScript 文件、CSS 文件等靜態資源非常有用,因為它們可以通過 URL 直接加載,提高了網頁加載速度和性能。
  3. 部署簡單性:當您部署應用程序時,只需將 public 資料夾中的內容部署到網頁伺服器(如 Apache、Nginx)的根目錄下,這樣您的網站可以立即提供訪問。這樣的佈署過程簡單明瞭。
  4. 組織性:將公開檔案存儲在 public 資料夾中可以使您的專案組織結構更加清晰和有序。您可以將靜態資源、上傳的檔案等都放在一個地方,易於管理和維護。

試想如果惡意使用者拿到你的 .env 檔案,等同於拿到 database 的帳號密碼,可以進資料庫查看會員的所有資料,那個資就完全外洩了!

有興趣的話可以查看自己有上線的網站被訪問紀錄,這是一個在 aws 上的練習專案,已經幾個月沒碰過,但每天還是有很多 http 訪問紀錄,其中包含一些想要獲取 .env 網站的傢伙!(不過也有可能是一些網站的爬蟲等等…)總之,擋下來就對惹!

https://ithelp.ithome.com.tw/upload/images/20231004/20162893zxQM0oJZry.png
https://ithelp.ithome.com.tw/upload/images/20231004/20162893IvgrEFuMsp.png

為什麼 database 不存完整路徑,要在最後傳資料給畫面時重新轉換?這樣不是很麻煩嗎?

  1. 把檔案位置分離:將文件路徑存儲為相對路徑或文件名而不是完整的絕對路徑,可以使您的應用程序更具靈活性。當需要修改檔案的位置或儲存檔案的方式時,不必更改每個路徑。
  2. 降低資料庫負擔:儲存完整的檔案路徑可能會增加資料庫的負擔,尤其是當有大量檔案需要管理時。資料庫的主要目的是存儲結構化的數據,而不是大型二進制文件。將文件路徑存儲在資料庫中可以降低資料庫的大小,提高性能。
  3. 路徑動態生成:有時,文件的存儲位置可能取決於不同的因素,例如不同的環境或用戶。在這種情況下,根據需要動態生成文件路徑可能更為靈活。
  4. 安全性:將完整的文件路徑存儲在資料庫中可能存在一些安全風險,因為某些文件系統路徑可能包含敏感信息。通過存儲相對路徑,您可以更好地控制訪問文件的權限。
  5. 簡化代碼:在前端,通常需要將存儲的相對路徑轉換為實際的文件 URL。這可以在代碼的一個地方進行轉換,以簡化代碼並提高可維護性。

雖然在某些情況下可能會感到麻煩,但這種方法通常可以提供更好的彈性、性能和安全性。


上一篇
後端專屬: API Resource Collection
下一篇
Laravel Mutator & Accessor / 修改取得圖片路徑的方式
系列文
Laravel 後端菜鳥可以知道的流程概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言